/*
 * Account.java
 *
 *  
 */
package org.msb.finance.data;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Currency;
import java.util.Date;
import java.util.List;

/**
 * The {@code Account} class is the abstract super class to all types of accounts maintained by the system. An account
 * in this system, primarily consists of:
 * <ul>
 * <li>a collection of {@link Transaction} objects;</li>
 * <li>some data fields such as name, account number, associated {@link Institution}, closed flag, etc...</li>
 * <li>a starting balance; and</li>
 * <li>a current balance.</li>
 * </ul>
 * 
 * Subclasses are defined to augment the above list.
 * 
 * @author Marc Boudreau
 * 
 */
public abstract class Account implements Comparable<Account> {

	/*
	 * A name to identify this account in the application. Each {@code Account} object must have a unique name.
	 */
	private String name;

	/*
	 * The account number for this account. This is kept for reference purposes only.
	 */
	private String accountNum;

	/*
	 * The institution where the account is held.
	 */
	private Institution institution;

	/**
	 * The transactions that occurred in this account.
	 */
	protected List<Transaction> transactions;

	/*
	 * The last reported balance for this account.
	 */
	private BigDecimal reportedBalance;

	/*
	 * The number of transactions that have not been reviewed for this account.
	 */
	private int unreviewedTransactions;

	/*
	 * The date of the last account update.
	 */
	private Date lastUpdated;

	/*
	 * A description of this account.
	 */
	private String description;

	/*
	 * A flag indicating if this account is now closed.
	 */
	private boolean closed;

	/*
	 * The currency of this account.
	 */
	private Currency currency;

	/*
	 * The starting balance of the account.
	 */
	private BigDecimal startBalance;

	/*
	 * 
	 */
	@SuppressWarnings("unused")
	private Account() {
		this(null);
	}

	/**
	 * 
	 * @param name
	 */
	public Account(String name) {
		this.transactions = new ArrayList<Transaction>();
		this.name = name;

		setStartBalance(new BigDecimal(0));
	}

	/**
	 * 
	 * @return s
	 */
	public String getName() {
		return this.name;
	}

	/*
	 * The name of an account is set in the constructor or by the renameAccount method of the Cabinet class. This method
	 * is provided with package access only to enable the renameAccount method to change the name field.
	 */
	void setName(String newName) {
		name = newName;
	}

	/**
	 * 
	 * @return s
	 */
	public String getAccountNumber() {
		return this.accountNum;
	}

	/**
	 * 
	 * @param accountNumber
	 */
	public void setAccountNumber(String accountNumber) {
		this.accountNum = accountNumber;
	}

	/**
	 * 
	 * @return s
	 */
	public Date getLastUpdate() {
		return this.lastUpdated;
	}

	/**
	 * 
	 * @param date
	 */
	public void setLastUpdate(Date date) {
		this.lastUpdated = date;
	}

	/**
	 * 
	 * @return s
	 */
	public int getUnreviewedTransactions() {
		return this.unreviewedTransactions;
	}

	/**
	 * 
	 * @param count
	 */
	public void setUnreviewedTransactions(int count) {
		this.unreviewedTransactions = count;
	}

	/**
	 * 
	 * @return s
	 */
	public BigDecimal getReportedBalance() {
		return this.reportedBalance;
	}

	/**
	 * 
	 * @param balance
	 */
	public void setReportedBalance(BigDecimal balance) {
		this.reportedBalance = balance;
	}

	/**
	 * 
	 * @return s
	 */
	public Institution getInstitution() {
		return this.institution;
	}

	/**
	 * 
	 * @param institution
	 */
	public void setInstitution(Institution institution) {
		/*
		 * Deal with the trivial case where the institution is being set to the same value.
		 */
		if (null == this.institution && null == institution || null != this.institution
				&& this.institution.equals(institution)) {
			return;
		}

		if (null != this.institution) {
			this.institution.removeAccount(this);
		}
		this.institution = institution;
		if (null != this.institution) {
			this.institution.addAccount(this);
		}
	}

	/**
	 * 
	 * @return s
	 */
	public String getDescription() {
		return this.description;
	}

	/**
	 * 
	 * @param description
	 */
	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * 
	 * @return s
	 */
	public boolean isClosed() {
		return this.closed;
	}

	/**
	 * 
	 * @param value
	 */
	public void setClosed(boolean value) {
		this.closed = value;
	}

	/**
	 * 
	 * @return s
	 */
	public Currency getCurrency() {
		return this.currency;
	}

	/**
	 * 
	 * @param currency
	 */
	public void setCurrency(Currency currency) {
		this.currency = currency;
	}

	/**
	 * 
	 * @param transaction
	 */
	public void addTransaction(Transaction transaction) {
		synchronized (transactions) {
			transactions.add(transaction);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(Account o) {
		if (this == o) {
			return 0;
		}

		if (null == this.name) {
			return null == o.name ? 0 : -1;
		} else if (null == o.name) {
			return 1;
		}

		return this.name.compareTo(o.name);
	}

	/**
	 * Returns the starting balance for this account. The starting balance is the balance amount of this account prior
	 * to the first transaction being counted.
	 * 
	 * @return A {@link BigDecimal} object containing the starting balance.
	 */
	public BigDecimal getStartBalance() {
		return startBalance;
	}

	/**
	 * Sets the starting balance for this account to the value of the provided {@link BigDecimal} object.
	 * 
	 * @param balance
	 *            A {@link BigDecimal} object containing the starting balance.
	 */
	public void setStartBalance(BigDecimal balance) {
		this.startBalance = balance;
	}

	/**
	 * Calculates the current balance of this account. The balance is calculated by adding the sum of all transaction
	 * amounts to the starting balance amount. The current balance is the balance that takes all recorded transactions
	 * into account, even if some of the transactions are post-dated to a future date.
	 * 
	 * @return A {@link BigDecimal} object containing the current balance amount.
	 */
	public BigDecimal getBalance() {
		BigDecimal balance = getStartBalance();

		for (Transaction t : this.transactions) {
			balance = balance.add(t.getAmount());
		}

		return balance;
	}
}
